# coding: utf8


import sys
import weakref
import gc
import time


def test_ref():
    class DemoClass1:
        pass

    a = DemoClass1()
    b = a
    c = [a, b, DemoClass1()]
    print(
        ' >>> a = DemoClass1()                      # 建立对DemoClass1()的引用a\n'
        ' >>> b = a                                 # 增加对DemoClass1()的引用b\n'
        ' >>> c = [a, b, DemoClass1()]              # 列表元素建立了自己的引用，新建对象DemoClass1()是新的引用\n'
        ' >>> sys.getrefcount(a)-1                  # a指向对象的引用为：a、b、c[0]、c[1], 不包括c[2]\n'
        f'{sys.getrefcount(a)-1} \n'
        f'id(a) = {id(a)}, id(c[2]) = {id(c[2])}    # c[2]与a引用了不同的对象'
    )

    del b
    print(
        ' >>> del b                     # 删除引用b， a所指向对象引用数减1\n'
        ' >>> sys.getrefcount(a)-1\n'
        f'{sys.getrefcount(a)-1}        # a指向对象剩下3个引用：a, c[0], c[1]'
    )

    del c[2]
    print(
        ' >>> del c[2]                  # 删除c[2]，不影响a指向对象的引用\n'
        ' >>> sys.getrefcount(a)-1\n'
        f'{sys.getrefcount(a)-1}'
    )


def test_container_ref():
    a = [1, 2, 3, 4, 5]
    b = a

    print(
        " >>> a = [1, 2, 3, 4, 5]                   # 创建容器对象并建立引用a， 每个元素引用各自的对象\n"
        " >>> b = a                                 # 变量b引用了a指向的列表\n"
        " >>> sys.getrefcount(a)-1                  # 列表对象的引用计数\n"
        f"{sys.getrefcount(a)-1}\n"
        f" sys.getrefcount(a[0])-1                  # 列表元素指向对象的引用计数（整数1的引用计数）\n"
        f"{sys.getrefcount(a[0])-1}\n"
        f" >>> [sys.getrefcount(x)-1 for x in a]    # 查看各个列表元素所指向对象的引用计数\n"
        f"{[sys.getrefcount(x)-1 for x in a]}       # 注意，迭代过程中增加了一个临时引用"
    )


def test_small_integer_ref():
    """
    在函数中，不仅仅是小整数，
    数字值都采用了常量引用方式，
    不可变对象元组也使用常量机制，
    应该是Python在函数中对不可变对象引用的优化!
    """
    class Demo:
        pass

    a = -5
    b = a
    x = 1000
    y = 1000
    z = 1000
    f1 = 3.1
    f2 = 3.1
    t1 = (2000,)
    t2 = (2000,)
    c1, c2 = Demo(), Demo()
    l1, l2 = [1], [1]

    print(
        " >>> a = -5                        # 创建小整数对象\n"
        " >>> sys.getrefcount(a)-1          # 显示整数-5的引用\n"
        f"{sys.getrefcount(a)-1}            # 引用计数较多，包括系统内很多使用-5的变量引用\n"
        f" >>> x = 10000                    # 创建小整数池之外的整数对象\n"
        f" >>> y = 10000\n"
        f" >>> sys.getrefcount(x)-1\n"
        f"{sys.getrefcount(x)-1}\n"
        f" >>> id(x), id(y)， id(z)         # x,y 指向了相同的对象， 为常数引用方式 \n"
        f"{id(x), id(y), id(z)} \n"
        f" >>> id(f1), id(f2)               # 浮点数对象，使用常数引用方式\n"
        f" {id(f1), id(f2)}\n"
        f" >>> id(t1), id(t2)               # 元组对象为不可变的，使用常数引用方式\n"
        f"{id(t1), id(t2)}\n"
        f" >>> id(l1), id(l2)               # 列表对象为可变的， 不能按照常数引用方式\n"
        f"{id(l1), id(l2)}\n"
        f" >>> id(u), id(v)                 # 普通类创建的对象为可变的，不能按照常数引用方式\n"
        f"{id(c1), id(c2)}\n"
    )


def test_weakref():
    class DemoClass2:
        def m1(self):
            return 'Demo Class2'

    a = DemoClass2()
    a1 = a
    b = weakref.ref(a)
    c = weakref.ref(a)

    print(
        f' >>> class DemoClass2:\n'
        f'         def m1(self):\n'
        f'         return "Demo Class2"\n'
        f'\n'
        f' >>> a = DemoClass2()     # 创建对象，赋予引用变量a\n'
        f' >>> a1 = a               # 增加引用a1\n'
        f' >>> b = weakref.ref(a)   # 建立弱引用b\n'
        f' >>> c = weakref.ref(a)   # 建立弱引用c\n'
        f' >>> sys.getrefcount(a)-1\n'
        f' {sys.getrefcount(a)-1}\n'
        f''
        f' >>> a.__weakref__， a1.__weakref      # 对象中存储了所建立的弱引用\n'
        f' ({a.__weakref__}, {a1.__weakref__})\n'
        f' >>> b， c\n'
        f' （{b}， {c})\n'
        f''
        f' >>> id(a), id(a1), id(b), id(c)      # 各个引用变量的id, 弱引用与引用是不同的变量\n'
        f' ({str(hex(id(a))).upper()}, {str(hex(id(a1))).upper()}, '
        f'{str(hex(id(b))).upper()}, {str(hex(id(c))).upper()})\n'
        f''
        f' >>> a.m1()       # 使用引用调用对象\n'
        f' {a.m1()}\n'
        f' >>> b().m1()     # 使用弱引用调用对象，需要先实例化，使用b()\n'
        f' {b().m1()}'
        )

    del b
    print(f' >>> del b      # 删除弱引用b, 不影响对象的引用计数\n'
          f' >>> sys.getrefcount(a) - 1\n'
          f' {sys.getrefcount(a)-1}'
          )

    # weakref to weakrefed object prohibited!
    # c = weakref.obj(b)

    a = 0
    a1 = 1
    print(f' >>> a = 0\n'
          f' >>> a1 = 1     # 赋予a, a1新的引用, 所指向对象DemoClass2()将被回收\n'
          f' >>> c          # 然后弱引用失去被引用对象，显示引用无效: dead\n'
          f' {c}            \n'     
          f' >>> c()        # 调用无效弱引用，返回None\n'
          f' {c()}'
          )
    # print(gc.garbage)


def test_list_cyclic_ref():
    # 列表循环引用时，Python采取了特别的处理
    # 使循环调用形成类似生成器的方式
    #
    a = [1]
    b = [2]
    a.append(b)
    b.append(a)

    print(
        f" >>> a = [1]\n"
        f" >>> b = [2]\n"
        f" >>> a.append(b)\n"
        f" >>> b.append(c)\n"
        f"sys.getrefcount(a) = {sys.getrefcount(a)}\n"
        f"sys.getrefcount(b) = {sys.getrefcount(b)}\n"
        f"gc.referrers(a)= {gc.get_referrers(a)}\n"
        # f"gc.get_referrers(c)= {gc.get_referrers(c)}\n"
        f"a[1][1][1] == b: {a[1][1][1] == b}"
        )

    # list a with infinite levels
    def getdeep(x, i=100):
        if i > 0:
            return getdeep(x[1], i-1)
        else:
            return x[1]

    print(f"getdeep(a): {getdeep(a)}")
    print(f"getdeep(b): {getdeep(b)}")

    del b
    print(a)
    print(gc.garbage)

    # 内存回收
    # del a, b
    # print(gc.garbage)
    # sys.getrefcount()
    # gc.collect()


def test_cross_ref():
    class DemoCrossRefClass:

        def __init__(self, x):
            self.x = x

        def __del__(self):
            print('object-{} is collected!'.format(self.x))

    obj1 = DemoCrossRefClass(100)
    obj2 = DemoCrossRefClass(200)

    obj1.x = obj2
    obj2.x = obj1

    id1 = hex(id(obj1)).upper()
    id2 = hex(id(obj2)).upper()
    print(
        f"id(obj1)={id1}, id(obj2)={id2}"
    )

    del obj1, obj2
    print(
        ' >>> del obj1, obj2\n'
    )

    time.sleep(2)
    tracked = gc.get_objects()
    print(
        "# check obj1, obj2 by gc.get_objects(): \n"
        " >>> tracked = gc.get_objects()\n"
        " >>> [ob for ob in tracked if id1[2:] in str(ob) or id2[2:] in str(ob)]\n"
        f"{[ob for ob in tracked if id1[2:] in str(ob) or id2[2:] in str(ob)]}"
        )
    print("仍然没有删除!")

    # print('use gc.collect() to collect')
    # gc.collect()
    #
    # tracked = gc.get_objects()
    # print(
    #     f"# check obj1, obj2 by gc.get_objects(): \n"
    #     f"{[ob for ob in tracked if id1[2:] in str(ob) or id2[2:] in str(ob)]}"
    # )
    # print("已经删除!")
